<?php

class Idcode {

    private static $r = array(
        '3', 'm', 'j', 'u', 'f', 'r', '4', 'd', 'z', 'x', '9', 'c', 'p', '5', 'k',
        'q', '7', 'w', 'e', '8', 's', '2', 'v', 'y', 't', 'n', '6', 'b', 'g', 'h');
    private static $b = 'a';
    private static $binLen = 30;
    private static $s = 7;
    private static $e = "xtvsmwp";

    public static function toSerialCode($id) {
        $buf = array_pad(array(), 32, '');

        $charPos = 32;

        while (($id / self::$binLen) > 0) {
            $ind = intval($id % self::$binLen);
            $buf[--$charPos] = self::$r[$ind];
            $id = intval($id / self::$binLen);
        }

        $buf[--$charPos] = self::$r[intval($id % self::$binLen)];

        $str = implode('', array_slice($buf, $charPos, (32 - $charPos)));

        if (strlen($str) < self::$s) {
            $sb = self::$b;
            for ($i = 1; $i < self::$s - strlen($str); $i++) {
                $sb .= self::$r[rand(0, self::$binLen - 1)];
            }
            $str .= $sb;
        }
        return self::mix($str);
    }

    public static function toFixedSerialCode($id) {
        $buf = array_pad(array(), 32, '');

        $charPos = 32;

        while (($id / self::$binLen) > 0) {
            $ind = intval($id % self::$binLen);
            $buf[--$charPos] = self::$r[$ind];
            $id = intval($id / self::$binLen);
        }

        $buf[--$charPos] = self::$r[intval($id % self::$binLen)];

        $str = implode('', array_slice($buf, $charPos, (32 - $charPos)));

        if (strlen($str) < self::$s) {
            $str .= self::$b;
            $str .= substr(self::$e, 0, self::$s - strlen($str));
        }
        return self::mix($str);
    }

    public static function codeToId($code) {
        $crc = substr($code, 0, 1);
        $sorted = self::resort(substr($code, 1), $crc);
        if ($crc !== self::crc($sorted)) {
            return 0;
        }
        $chs = str_split($sorted);
        $res = 0;
        for ($i = 0; $i < count($chs); $i++) {
            $ind = 0;
            for ($j = 0; $j < self::$binLen; $j++) {
                if ($chs[$i] == self::$r[$j]) {
                    $ind = $j;
                    break;
                }
            }
            if ($chs[$i] == self::$b) {
                break;
            }
            if ($i > 0) {
                $res = $res * self::$binLen + $ind;
            } else {
                $res = $ind;
            }
        }
        return $res;
    }

    private static function crc($str) {
        $chs = str_split($str);
        $asc = 0;
        foreach ($chs as $ch) {
            $asc += ord($ch);
        }
        $asc %= self::$binLen;
        return self::$r[$asc];
    }

    private static function mix($str) {
        $chs = str_split($str);
        $crc = self::crc($str);
        $new = '';
        switch (ord($crc) % 2) {
            case 0: // 奇偶位混排
                for ($i = 0; $i < count($chs); $i++) {
                    if ($i % 2 == 0) {
                        $new .= $chs[$i];
                    }
                }
                for ($i = 0; $i < count($chs); $i++) {
                    if ($i % 2 == 1) {
                        $new .= $chs[$i];
                    }
                }
                break;
            case 1: // 前后对调
                for ($i = count($chs) - 1; $i >= (int) (count($chs) / 2); $i--) {
                    $new .= $chs[$i];
                }
                for ($i = 0; $i < (int) (count($chs) / 2); $i++) {
                    $new .= $chs[$i];
                }
                break;
        }
        return $crc . $new;
    }

    private static function resort($str, $crc) {
        $chs = str_split($str);
        $count = count($chs);
        $new = '';
        switch (ord($crc) % 2) {
            case 0: // 奇偶位混排
                $half = ceil($count / 2);
                if (count($chs) % 2 == 0) {
                    for ($i = 0; $i < $half; $i++) {
                        $new .= $chs[$i] . $chs[$half + $i];
                    }
                } else {
                    for ($i = 0; $i < $half - 1; $i++) {
                        $new .= $chs[$i] . $chs[$half + $i];
                    }
                    $new .= $chs[$half - 1];
                }
                break;
            case 1: // 前后对调
                $half = ceil($count / 2);
                for ($i = $half; $i < $count; $i++) {
                    $new .= $chs[$i];
                }
                for ($i = $half - 1; $i >= 0; $i--) {
                    $new .= $chs[$i];
                }
                break;
        }
        return $new;
    }

}
